#include <cppunit/config/SourcePrefix.h>
#include "TestPQL.h"
#include <string>

void TestPQL::setUp() {

	//Custom setup AST - the same one used in TestDesignExtractor
	n[0] = new Node(new Program());
	n[1] = new Node(new Procedure(0));
	n[2] = new Node(new StmtList());
	n[3] = new Node(new Assign(),1);
	n[4] = new Node(new Variable(0),1);
	n[5] = new Node(new Plus(),1);
	n[6] = new Node(new Plus(),1);
	n[7] = new Node(new Variable(1),1);
	n[8] = new Node(new Constant(3),1);
	n[9] = new Node(new Variable(2),1);
	n[10] = new Node(new While(),2);
	n[11] = new Node(new Variable(3),2);
	n[12] = new Node(new StmtList());
	n[13] = new Node(new Assign(),3);
	n[14] = new Node(new Variable(0),3);
	n[15] = new Node(new Variable(2),3);
	n[16] = new Node(new Assign(),4);
	n[17] = new Node(new Variable(2),4);
	n[18] = new Node(new Plus(),4);
	n[19] = new Node(new Variable(1),4);
	n[20] = new Node(new Constant(3),4);

	pql_ast.setRoot(n[0]);
	n[0]->addChildNode(n[1]);
	n[1]->addChildNode(n[2]);
	n[2]->addChildNode(n[3]);
	n[2]->addChildNode(n[10]);
	n[3]->addChildNode(n[4]);
	n[3]->addChildNode(n[5]);
	n[5]->addChildNode(n[6]);
	n[5]->addChildNode(n[9]);
	n[6]->addChildNode(n[7]);
	n[6]->addChildNode(n[8]);
	n[10]->addChildNode(n[11]);
	n[10]->addChildNode(n[12]);
	n[12]->addChildNode(n[13]);
	n[12]->addChildNode(n[16]);
	n[13]->addChildNode(n[14]);
	n[13]->addChildNode(n[15]);
	n[16]->addChildNode(n[17]);
	n[16]->addChildNode(n[18]);
	n[18]->addChildNode(n[19]);
	n[18]->addChildNode(n[20]);

	pql_varTable = VarTable();
	pql_varTable.addVar("x", 1); //idx 0
	pql_varTable.addVar("y", 1); //idx 1
	pql_varTable.addVar("z", 1); //idx 2
	pql_varTable.addVar("x", 3);
	pql_varTable.addVar("z", 3);
	pql_varTable.addVar("z", 4);
	pql_varTable.addVar("y", 4);

	pql_follows = Follows();
	pql_follows.addFollows(1,2);
	pql_follows.addFollows(3,4);

	pql_parent = Parent();
	pql_parent.addParent(2, 3);
	pql_parent.addParent(2, 4);

	pql_uses = Uses();
	pql_uses.addUsesStatement(1, 1);
	pql_uses.addUsesStatement(1, 2);
	pql_uses.addUsesStatement(2, 1);
	pql_uses.addUsesStatement(2, 2);
	pql_uses.addUsesStatement(3, 2);
	pql_uses.addUsesStatement(4, 1);

	pql_modifies = Modifies();
	pql_modifies.addStmtModifies(1, 0);
	pql_modifies.addStmtModifies(2, 0);
	pql_modifies.addStmtModifies(2, 2);
	pql_modifies.addStmtModifies(3, 0);
	pql_modifies.addStmtModifies(4, 1);

	pql_deTable = AST::getDesignEntityTable();
}

void TestPQL::tearDown() {
}

CPPUNIT_TEST_SUITE_REGISTRATION( TestPQL );

void TestPQL::testPQL(){
	string query1 = "assign a; variable v; Select a such that Modifies (a,v)"; // ans = 1, 3, 4
	string query2 = "assign a; Select a such that Uses (a, \"y\")"; // ans = 1, 4
	string query3 = "stmt s; Select s such that Parent(s, 3)"; // ans = 2
	string query4 = "stmt s; Select s such that Parent*(s,4)"; // ans = 2
	string query5 = "stmt s; Select s such that Follows (1, s)"; // ans = 2
	string query6 = "stmt s; Select s such that Follows* (s, 4)"; // ans = 3
	string query7 = "assign a; Select a such that Uses (a, \"y\") pattern a(\"z\",_)"; //ans = 4
	string query8 = "stmt s; Select s"; // ans = 1, 2, 3, 4
	string query9 = "assign a; Select a"; // ans = 1, 3, 4
	string query10 = "while w; Select w"; // ans = 2
	string query11 = "variable v; Select v"; // ans = x, y, z
	string query12 = "constant c; Select c"; // ans = 3
	string query13 = "prog_line n; Select n"; // ans = 1, 2, 3, 4
	
	cout << endl <<"TestPQL starts" << endl;
	QueryEvaluator qe = QueryEvaluator(pql_ast, pql_deTable, pql_follows, pql_modifies, pql_parent, pql_uses, pql_varTable);
	list<string> results;
	int resultSize;
	
	QueryPreprocessor qp1 = QueryPreprocessor(query1);
	CompleteQuery *qc1 = qp1.buildQuery();
	results = qe.evaluateCompleteQuery(qc1);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("3 results found", 3, resultSize);

	QueryPreprocessor qp2 = QueryPreprocessor(query2);
	CompleteQuery *qc2 = qp2.buildQuery();
	results = qe.evaluateCompleteQuery(qc2);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("2 results found", 2, resultSize);

	QueryPreprocessor qp3 = QueryPreprocessor(query3);
	CompleteQuery *qc3 = qp3.buildQuery();
	results = qe.evaluateCompleteQuery(qc3);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);

	QueryPreprocessor qp4 = QueryPreprocessor(query4);
	CompleteQuery *qc4 = qp4.buildQuery();
	results = qe.evaluateCompleteQuery(qc4);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);

	QueryPreprocessor qp5 = QueryPreprocessor(query5);
	CompleteQuery *qc5 = qp5.buildQuery();
	results = qe.evaluateCompleteQuery(qc5);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);

	QueryPreprocessor qp6 = QueryPreprocessor(query6);
	CompleteQuery *qc6 = qp6.buildQuery();
	results = qe.evaluateCompleteQuery(qc6);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);

	QueryPreprocessor qp7 = QueryPreprocessor(query7);
	CompleteQuery *qc7 = qp7.buildQuery();
	results = qe.evaluateCompleteQuery(qc7);
	resultSize = results.size();
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);

	QueryPreprocessor qp8 = QueryPreprocessor(query8);
	CompleteQuery *qc8 = qp8.buildQuery();
	results = qe.evaluateCompleteQuery(qc8);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("4 result found", 4, resultSize);

	QueryPreprocessor qp9 = QueryPreprocessor(query9);
	CompleteQuery *qc9 = qp9.buildQuery();
	results = qe.evaluateCompleteQuery(qc9);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("3 result found", 3, resultSize);

	QueryPreprocessor qp10 = QueryPreprocessor(query10);
	CompleteQuery *qc10 = qp10.buildQuery();
	results = qe.evaluateCompleteQuery(qc10);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);
	
	QueryPreprocessor qp11 = QueryPreprocessor(query11);
	CompleteQuery *qc11 = qp11.buildQuery();
	results = qe.evaluateCompleteQuery(qc11);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("3 result found", 3, resultSize);
	
	QueryPreprocessor qp12 = QueryPreprocessor(query12);
	CompleteQuery *qc12 = qp12.buildQuery();
	results = qe.evaluateCompleteQuery(qc12);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("1 result found", 1, resultSize);
	
	QueryPreprocessor qp13 = QueryPreprocessor(query13);
	CompleteQuery *qc13 = qp13.buildQuery();
	results = qe.evaluateCompleteQuery(qc13);
	resultSize = results.size();//printResults(results);
	CPPUNIT_ASSERT_EQUAL_MESSAGE("4 result found", 4, resultSize);
}

void TestPQL::printResults(list<string> results){
	for (list<string>::iterator it = results.begin(); it != results.end(); ++it){
		cout << *it << endl;
	}
}